package persistence.db;

import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Iterator;

import persistence.dao.AbstractDAO;
import persistence.model.AbstractModel;

/**
 * Ensemble de données.
 */
public class DataSet implements Iterable<AbstractModel> {

	public static int openCursors = 0; /* TODO A supprimer - indique le nombre de ResultSet ouverts */

	public final static String ASC_ORDER = "ASC";
	public final static String DESC_ORDER = "DESC";

	private AbstractDAO dao;
	private String conditions = "";
	private String orderConditions = "";
	private String orderDirection = "";

	private ResultSet result;

	/**
	 * @param dao
	 *            Data Access Object du modèle sur lequel va porter la requête.
	 */
	public DataSet(AbstractDAO dao) {
		this.dao = dao;
	}

	/**
	 * Construit un nouvel ensemble de données à partir d'un ensemble déjà
	 * existant en lui ajoutant une condition de filtrage.
	 * 
	 * @param oldDataSet
	 *            Ancien ensemble de données
	 * @param condition
	 *            Condition de filtrage
	 */
	private DataSet(DataSet oldDataSet, String condition) {
		this.dao = oldDataSet.dao;
		this.conditions = oldDataSet.conditions + condition;
	}

	public DataSet(DataSet oldDataSet, String orderCondition,
			String orderDirection) {
		this.dao = oldDataSet.dao;
		this.conditions = oldDataSet.conditions;
		this.orderConditions = orderCondition;
		this.orderDirection = orderDirection;
	}

	/**
	 * @param condition
	 *            Condition de filtrage
	 * @return Un nouvel ensemble de données filtré.
	 */
	public DataSet filter(String condition) {
		return new DataSet(this, condition);
	}

	public DataSet order(String orderCondition, String orderDirection) {
		return new DataSet(this, orderCondition, orderDirection);
	}

	/*
	 * (non-Javadoc) Construit un itérateur sur l'ensemble de données filtré
	 * 
	 * @see java.lang.Iterable#iterator()
	 */

	public AbstractModel get(int id) {
		DataSet singleObjectSet = filter("ID_" + dao.getTableName() + " = "
				+ id);

		return singleObjectSet.getFirst();
	}

	/**
	 * Construit une ArrayList contenant tous les éléments de l'ensemble de
	 * données.
	 * 
	 * @return Liste de tous les éléments de l'ensemble.
	 */
	public ArrayList<AbstractModel> toArrayList() {
		ArrayList<AbstractModel> list = new ArrayList<AbstractModel>();

		for (AbstractModel modelInstance : this) {
			list.add(modelInstance);
		}

		return list;
	}

	/**
	 * Retourne le premier élément de l'ensemble
	 */
	public AbstractModel getFirst() {
		Iterator<AbstractModel> it = iterator();
		AbstractModel first = it.next();
		DatabaseConnector.getInstance().closeResultSet(result); openCursors--;
		return first;
	}

	public Iterator<AbstractModel> iterator() {
		String query;

		/* Construit la requete SQL */
		query = "SELECT * FROM " + dao.getTableName();

		/* Ajoute la clause WHERE si necessaire */
		if (!conditions.isEmpty()) {
			query = query + " WHERE " + conditions;
		}

		System.out.println(query); /* TODO Supprimer - affichage de la requete */

		/* Execute la requete */
		result = DatabaseConnector.getInstance().execute(query);
		openCursors++;
		
		return new ModelIterator(result);
	}

	/**
	 * Itérateur sur un ensemble de données
	 */
	public class ModelIterator implements Iterator<AbstractModel> {

		ResultSet result;
		boolean hasNext;

		public ModelIterator(ResultSet result) {
			this.result = result;

			try {
				hasNext = result.next(); /* Charger le premier resultat */

			} catch (SQLException e) {
				System.err.println("Exception chargement du premier element");
				e.printStackTrace();
			}
		}

		/*
		 * (non-Javadoc)
		 * 
		 * @see java.util.Iterator#hasNext()
		 */
		public boolean hasNext() {
			if (!hasNext) {
				DatabaseConnector.getInstance().closeResultSet(result); openCursors--;
			}
			
			return hasNext;
		}

		/*
		 * (non-Javadoc)
		 * 
		 * @see java.util.Iterator#next()
		 */
		public AbstractModel next() {
			try {
				AbstractModel m = dao.makeModel(result);
				hasNext = result.next();

				return m;

			} catch (SQLException e) {
				System.err.println("Exception chargement de l'element suivant");
				e.printStackTrace();
			}

			return null;
		}

		/*
		 * (non-Javadoc)
		 * 
		 * @see java.util.Iterator#remove()
		 */
		public void remove() {
			// TODO Voir si on va gérer la suppression dans l'itérateur (ça
			// pourrait être utile)

		}

		/* Destructeur */
		protected void finalize() throws Throwable {
			/*
			 * Refermer le curseur si jamais il est resté ouvert (OJDBC ne
			 * referme pas les curseurs qui ne servent plus).
			 */
			//result.close();
			
			super.finalize();
		}
	}
}
